using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;

namespace Universe.WinForms.Settings
{
    public class SettingsScope
    {
        [Flags]
        public enum CommonScope
        {
            CurrentUser = 32768,
            AllUsers = 65536,

            ExecutableName = 1,
            ExecutableCompany = 2,
            ExecutableProduct = 4,
            ExecutableVersionMajor = 8,
            ExecutableVersionMinor = 16,
            ExecutableVersionBuild = 32,
            ExecutableVersionRevision = 64,
            
            SchemaCompany = 128,
            SchemaProduct = 256,
            SchemaNamespace = 512,
            SchemaTypeName = 1024,
            SchemaVersionMajor = 2048,
            SchemaVersionMinor = 4096,
            SchemaVersionBuild = 8192,
            SchemaVersionRevision = 16384,
        }

        CommonScope _commonScope;
        string _customScope;

        public SettingsScope(CommonScope parts, string instanceId)
        {
            _commonScope = parts;
            _customScope = instanceId;
        }

        public SettingsScope(CommonScope parts)
        {
            _commonScope = parts;
        }

        public CommonScope Common
        {
            get { return _commonScope; }
        }

        public string CustomScope
        {
            get { return _customScope; }
        }

        static string GetAssemblyCompany(Assembly assembly)
        {
            object[] attrs = assembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
            return
                (attrs != null && attrs.Length > 0)
                    ? (attrs[0] as AssemblyCompanyAttribute).Company
                    : null;
        }

        static string GetAssemblyProduct(Assembly assembly)
        {
            object[] attrs = assembly.GetCustomAttributes(typeof(AssemblyProductAttribute), false);
            return
                (attrs != null && attrs.Length > 0)
                    ? (attrs[0] as AssemblyProductAttribute).Product
                    : null;
        }

        static string GetAssemblyVersion(Assembly assembly, bool major, bool minor, bool build, bool revision)
        {
            Version ver = assembly.GetName().Version;
            List<int> parts = new List<int>();
            if (major)
                parts.Add(ver.Major);

            if (minor)
                parts.Add(ver.Minor);

            if (build)
                parts.Add(ver.Build);

            if (revision)
                parts.Add(ver.Revision);

            StringBuilder ret = new StringBuilder();
            foreach (int i in parts)
            {
                if (ret.Length > 0)
                    ret.Append('.');

                ret.Append(i);
            }

            return ret.ToString();
        }

        public string GetFileName(Type schema)
        {
            List<string> parts = new List<string>();
            SettingsScope _settingsScope = this;

            bool hasExecutableCompany = (_settingsScope.Common & SettingsScope.CommonScope.ExecutableCompany) == SettingsScope.CommonScope.ExecutableCompany;
            if (hasExecutableCompany)
            {
                string part = GetAssemblyCompany(Assembly.GetEntryAssembly());
                parts.Add(
                    part == null || part.Trim().Length == 0
                        ? "Unknown Company"
                        : part);
            }

            bool hasExecutableProduct = (_settingsScope.Common & SettingsScope.CommonScope.ExecutableProduct) == SettingsScope.CommonScope.ExecutableProduct;
            if (hasExecutableProduct)
            {
                string part = GetAssemblyProduct(Assembly.GetEntryAssembly());
                parts.Add(
                    part == null || part.Trim().Length == 0
                        ? "Unknown Product"
                        : part);
            }

            bool hasExecutableName = (_settingsScope.Common & SettingsScope.CommonScope.ExecutableName) == SettingsScope.CommonScope.ExecutableName;
            if (hasExecutableName)
                parts.Add(Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location));

            string entryVersion = GetAssemblyVersion(
                Assembly.GetEntryAssembly(),
                (_settingsScope.Common & SettingsScope.CommonScope.ExecutableVersionMajor) == SettingsScope.CommonScope.ExecutableVersionMajor,
                (_settingsScope.Common & SettingsScope.CommonScope.ExecutableVersionMinor) == SettingsScope.CommonScope.ExecutableVersionMinor,
                (_settingsScope.Common & SettingsScope.CommonScope.ExecutableVersionBuild) == SettingsScope.CommonScope.ExecutableVersionBuild,
                (_settingsScope.Common & SettingsScope.CommonScope.ExecutableVersionRevision) == SettingsScope.CommonScope.ExecutableVersionRevision);

            if (entryVersion.Length > 0)
            {
                if (hasExecutableCompany || hasExecutableName || hasExecutableProduct)
                    parts[parts.Count - 1] += " " + entryVersion;
                else
                    parts.Add(entryVersion);
            }

            bool hasSC = (_settingsScope.Common & SettingsScope.CommonScope.SchemaCompany) == SettingsScope.CommonScope.SchemaCompany;
            if (hasSC)
            {
                string part = GetAssemblyCompany(schema.Assembly);
                parts.Add(
                    part == null || part.Trim().Length == 0
                        ? "Unknown Company"
                        : part);
            }

            bool hasSP = (_settingsScope.Common & SettingsScope.CommonScope.SchemaProduct) == SettingsScope.CommonScope.SchemaProduct;
            if (hasSP)
            {
                string part = GetAssemblyProduct(schema.Assembly);
                parts.Add(
                    part == null || part.Trim().Length == 0
                        ? "Unknown Product"
                        : part);
            }


            string typeName = string.Empty;

            if ((_settingsScope.Common & SettingsScope.CommonScope.SchemaNamespace) == SettingsScope.CommonScope.SchemaNamespace)
                typeName = schema.Namespace;

            if ((_settingsScope.Common & SettingsScope.CommonScope.SchemaTypeName) == SettingsScope.CommonScope.SchemaTypeName)
                typeName += (typeName.Length == 0 ? string.Empty : ".") + schema.Name;

            if (typeName.Length > 0)
                parts.Add(typeName);

            string schemaVersion = GetAssemblyVersion(
                schema.Assembly,
                (_settingsScope.Common & SettingsScope.CommonScope.SchemaVersionMajor) == SettingsScope.CommonScope.SchemaVersionMajor,
                (_settingsScope.Common & SettingsScope.CommonScope.SchemaVersionMinor) == SettingsScope.CommonScope.SchemaVersionMinor,
                (_settingsScope.Common & SettingsScope.CommonScope.SchemaVersionBuild) == SettingsScope.CommonScope.SchemaVersionBuild,
                (_settingsScope.Common & SettingsScope.CommonScope.SchemaVersionRevision) == SettingsScope.CommonScope.SchemaVersionRevision);

            if (entryVersion.Length > 0)
            {
                if (hasSC || hasSP)
                    parts[parts.Count - 1] += " " + schemaVersion;
                else
                    parts.Add(schemaVersion);
            }


            if (_settingsScope.CustomScope != null && _settingsScope.CustomScope.Trim().Length > 0)
                parts.Add(_settingsScope.CustomScope);

            parts[parts.Count - 1] += ".settings";
            string pathBase =
                (_settingsScope.Common & SettingsScope.CommonScope.CurrentUser) == SettingsScope.CommonScope.CurrentUser
                    ? Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
                    : Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);

            StringBuilder ret = new StringBuilder(pathBase);
            foreach (string part in parts)
            {
                if (ret[ret.Length - 1] != '\\'
                    && ret[ret.Length - 1] != '/'
                    && ret[ret.Length - 1] != Path.DirectorySeparatorChar)
                {
                    ret.Append(Path.DirectorySeparatorChar);
                }

                ret.Append(part);
            }

            return ret.ToString();
        }

        public static readonly SettingsScope Default = new SettingsScope(CommonScope.ExecutableCompany | CommonScope.ExecutableProduct | CommonScope.SchemaNamespace | CommonScope.SchemaTypeName);
    }
}
